<?xml version="1.0" encoding="UTF-8"?>
<html>
    <head>
        <link type="text/css" rel="stylesheet" href="./css/template.css" />
        <link type="text/css" rel="stylesheet" href="./css/SyntaxHighlighter.css" />
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>Les méthodes natives</title>
        <script language="javascript" src="./js/shInit.js" />
        <script language="javascript" src="./js/shCore.js" />
        <script language="javascript" src="./js/shBrushCpp.js" />
        <script language="javascript" src="./js/shBrushCSharp.js" />
        <script language="javascript" src="./js/shBrushCss.js" />
        <script language="javascript" src="./js/shBrushDelphi.js" />
        <script language="javascript" src="./js/shBrushJava.js" />
        <script language="javascript" src="./js/shBrushJScript.js" />
        <script language="javascript" src="./js/shBrushPhp.js" />
        <script language="javascript" src="./js/shBrushPython.js" />
        <script language="javascript" src="./js/shBrushRuby.js" />
        <script language="javascript" src="./js/shBrushSql.js" />
        <script language="javascript" src="./js/shBrushVb.js" />
        <script language="javascript" src="./js/shBrushXml.js" />
        <keywords>fonctions en C</keywords>
        <author>Emmanuel BOUBLI</author>
    </head>
    <body>
        <div class="chapter">
            <h2>Introduction</h2>
            <p>Vous êtes certainement convaincu que le code écrit en langage Java a un certain nombre d'avantages sur le
				code écrit dans les langages comme C ou C++, même en ce qui concerne les applications spécifiques à une plate-forme.
				Il n'est bien sûr pas question ici de portabilité.
				Tandis qu'une solution "100% pure Java" est en principe sympathique, pour être réaliste, en ce qui concerne
				une application,
				il existe des situations où vous voudrez écrire (ou utiliser) du code écrit dans un autre langage( un tel
				code est appelé code natif).</p>
            <p>Il y a trois raisons évidentes pour lesquelles ce peut être le bon choix:</p>
            <ul>
                <li>Vous disposez d'une quantité substantielle de code testé et débogué, disponible dans cet autre
					langage. Le portage du code en langage Java prendrait beaucoup de temps et le code résultant devrait être de nouveau
					testé et débogué.</li>
                <li>Votre application nécessite l'accès à des unités ou des fonctionnalitées système et l'utilisation de
					la technologie Java serait au mieux fastidieuse, et au pire impossible.</li>
                <li>La vitesse d'exécution du code est essentielle.</li>
            </ul>
            <p>Si vous êtes dans l'une de ces trois situations, il peut être judicieux d'appeler le code natif à partir de
				programmes écrits en Java.
				Voyons maintenant comment mettre en oeuvre le code natif</p>
        </div>
        <div class="chapter">
            <h2>Appeler une fonction C à partir du langage Java</h2>
            <div class="subChapter">
                <p>
                    Supposons que vous ayez une fonction C qui réalise une tâche que vous appréciez, et que pour une
					raison ou une autre, vous vouliez vous dispenser de la réimplémenter en langage Java.
					Pour les besoins de la démonstration, nous allons admettre qu'il s'agit de l'incontournable fonction printf.
					Vous voulez pouvoir appeler printf à partir de vos programmes. Le langage Java utilise le mot clé
                    <i>native</i>
                    , et il est évident que vous devrez encapsuler la fonction
                    <i>printf</i>
                    dans une classe.
					Vous pouvez parfaitement écrire quelque chose comme:
                </p>
                <textarea name="code" class="java">public class Printf
					{
					public native String printf(String s);
					}</textarea>
                <p>Vous pourrez en  fait compiler cette classe. Mais quand vous voudrez l'utiliser dans un
					programme, la machine virtuelle va vous dire qu'elle ne peut pas trouver printf. Ensuite, elle va émettre une erreur
					UnsatisfiedLinkError. L'astuce consiste donc à fournir les informations d'exécution nécessaires pour qu'elle puisse lier
					cette classe.
					Comme vous le verrez bientôt, avec le JDK cela implique un processus en trois étapes:</p>
                <ol>
                    <li>Générer un pseudo-code C pour une fonction qui fait la translation entre l'appel de la
						plate-forme Java et la fonction C réelle.
						Le pseudo-code réalise cette translation en prenant les informations de paramètres de la pile de la
						machine virtuelle, pour les passer à la fonction C compilée.</li>
                    <li>Créer une bibliothèque partagée spéciale et exporter le pseudo-code à partir de celle-ci.</li>
                    <li>
                        Utiliser une méthode spéciale, appelée
                        <i>System.loadLibrary</i>
                        pour demander à
						l'environnement d'exécution Java de charger la bibliothèque à partir de l'étape 2.
                    </li>
                </ol>
                <p>Nous allons maintenant vous montrer comment exécuter ces étapes différents exemples, en partant
					d'un cas spécial d'utilisation triviale de printf pour finir avec un exemple réaliste impliquant la fonction base de
					registres pour des fonctions dépendantes de la plate-forme Java.</p>
            </div>
            <div class="subChapter">
                <h3>Travailler avec la fonction printf</h3>
                <p>
                    Nous allons commencer par la situation virtuellement la plus simple possible d'utilisation de
                    <i>printf</i>
                    : l'appel d'une méthode native qui imprime le message "Hello, Native World".
					En fait, nous n'allons même pas exploiter les fonctionnalités de formatage de
                    <i>printf</i>
                    !
					C'est toutefois une excellente façon pour vous de vérifier que votre compilateur C fonctionne
					comme prévu avant de tenter l'implémentation de méthodes natives plus élaborées.
                </p>
                <p>Comme nous l'avons dit précédemment, vous devez déclarer la méthode native dans une classe.
					Le mot clé native avertit le compilateur que la méthode sera définie externe. Bien entendu, les méthodes
					natives ne contiennent aucun code en langage Java, et l'en-tête de la méthode est immédiatement suivi du point-virgule
					de fin.
					Cela signifie, comme vous l'avez vu dans l'exemple plus haut, que les déclarations de méthodes natives
					ont un aspect analogue à celui des déclarations de méthodes abstraites.</p>
                <textarea name="code" class="java">class HelloNative
					{
					public native static void greeting();
					...
					}</textarea>
                <p>Dans cet exemple particulier, remarquez que nous déclarons également la méthode native comme static.
					Les méthodes natives peuvent être statiques et non statiques. Cette méthode ne prend pas d'arguments;
					nous ne voulons pas pour l'instant nous préoccuper du passage de paramètres.</p>
                <p>Ecrivez ensuite une fonction C correspondante. Vous devez nommer cette fonction exactement comme
					l'attend l'environnement d'exécution de Java c'est-à-dire:</p>
                <ul>
                    <li>Utilisez le nom complet de méthode Java, comme HelloNative.greeting.</li>
                    <li>Remplacez chaque point par un caractère de soulignement, et faites précéder du préfixe Java_. Par
						exemple,Java_HelloNative_greeting.</li>
                    <li>Si le nom de la classe contient des caractères qui ne sont ni des chiffres ni des lettres ASCII, par
						exemple, '_', '$', ou des caractères Unicode '\u007F', remplacez-les par _0xxxx, où xxxx est la séquence de quatre
						chiffres hexadécimaux de la valeur Unicode du caractère.</li>
                </ul>
                <p>En réalité, personne ne fait cela manuellement; exécutez plutôt l'utilitaire javah, qui génère
					automatiquement les noms de fonctions.</p>
                <h4>Passage de types primitifs</h4>
                <p>Pour le passage de valeurs numériques entre C et Java, vous devez savoir quels types
					correspondent à quels autres. Par exemple, si C possède des types de données appelés int et long, leur implémentation
					est dépendante de la plate-forme. Sur certaines plates-formes, les types entiers font 16 bits, alors que sur d'autres
					ils font 32 bits. Pour cette raison, la JNI(Java Native Interface) définit les types jint, jlong, ect...
					Voici un tableau regroupant les correspondances entres les types natifs(C) et Java.</p>
                <img src="./images/01.gif" />
                <p>Voyons un exemple pour mieux comprendre.
					On va définir et utiliser une méthode native qui ajoute deux entiers et renvoie le résultat de
					l'addition.</p>
                <textarea name="code" class="java">class TestJNI1 {
					public native int ajouter(int a, int b);
					static {
					System.loadLibrary("mabibjni");
					}
					public static void main(String[] args) {
					TestJNI1 maclasse = new TestJNI1();
					System.out.println("2 + 3 = " + maclasse.ajouter(2,3));
					}
					}</textarea>
                <p>La déclaration de la méthode n'a rien de particulier hormis le modificateur native. La signature
					de la fonction dans le fichier .h tient des paramètres.</p>
                <p>Il suffit ensuite écrire l'implémentation du code natif.</p>
                <textarea name="code" class="c">
                    #include
                    <jni.h>
                        #include
                        <stdio.h>#include "TestJNI2.h"
							JNIEXPORT jint JNICALL Java_TestJNI2_ajouter
							(JNIEnv *env, jobject obj, jint a, jint b)
							{
							return a + b;
							}</stdio.h>
                    </jni.h>
                </textarea>
                <p>Il faut ensuite compiler le code.
					Il faut définir le fichier .def.
					La commande ci-dessous permet de construire une bibliothèque. Cette dernière va contenir les
					fonctions natives des deux classes Java précédemment définies.</p>
                <div class="quote">
                    <span class="cmd_line_lvl">$&gt;</span>
                    <span class="cmd_line">EXPORTS Java_TestJNI2_ajouter</span>
                </div>
            </div>
            <div class="subChapter">
                <h3>Passage d'objets</h3>
                <p>
                    Les objets sont passés par référence en utilisant une variable de type
                    <i>jobject</i>
                    .
					Plusieurs autres type sont prédéfinis par JNI pour des objets fréquemment utilisés :
                </p>
                <img src="./images/02.gif" />
                <p>Voici un exemple qui concatène deux chaînes de caractères:</p>
                <textarea name="code" class="java">class TestJNI3 {
					public native String concat(String a, String b);
					static {
					System.loadLibrary("mabibjni");
					}
					public static void main(String[] args) {
					TestJNI3 maclasse = new TestJNI3();
					System.out.println("abc + cde = " + maclasse.concat("abc","cde"));
					}
					}</textarea>
                <p>La déclaration de la fonction native dans le fichier TestJNI3.h est la suivante :</p>
                <textarea name="code" class="c">/*
					* Class:     TestJNI3
					* Method:    concat
					* Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
					*/
					JNIEXPORT jstring JNICALL Java_TestJNI3_concat
					(JNIEnv *, jobject, jstring, jstring);</textarea>
                <p>
                    Pour utiliser les paramètres de type
                    <i>jstring</i>
                    dans le code natif,
					il faut les transformer en utilisant des fonctions proposées par l'interface de type JNIEnv car le type String de Java n'est pas directement
					compatible avec les chaînes de caractères C (char *).
					Il existe des fonctions pour transformer des chaînes codées en UTF-8 ou en Unicode.
                </p>
                <p>Les méthodes pour traiter les chaînes au format UTF-8 sont :</p>
                <ul>
                    <li>
                        la méthode
                        <i>GetStringUTFChars()</i>
                        permet de convertir une chaîne de caractères Java en une chaîne de caractères de type C.
                    </li>
                    <li>
                        la méthode
                        <i>NewStringUTF()</i>
                        permet de demander la création d'une nouvelle chaîne de caractères.
                    </li>
                    <li>
                        la méthode
                        <i>GetStringUTFLength()</i>
                        permet de connaître la taille de la chaîne de caractères.
                    </li>
                    <li>
                        la méthode
                        <i>ReleaseStringUTFChars()</i>
                        permet de demander la libération des ressources allouées pour la chaîne de caractères dès que celle
						ci n'est plus utilisée.
						Son utilisation permet d'éviter des fuites mémoire.
                    </li>
                </ul>
                <p>
                    Les méthodes équivalentes pour les chaînes de caractères au format Unicode sont :
                    <i>GetStringChars()</i>
                    ,
                    <i>NewString()</i>
                    ,
                    <i>GetStringUTFLength()</i>
                    et
                    <i>ReleaseStringChars()</i>
                    :
                </p>
                <textarea name="code" class="c">
                    #include
                    <jni.h>
                        #include
                        <stdio.h>#include "TestJNI3.h"
							JNIEXPORT jstring JNICALL Java_TestJNI3_concat
							(JNIEnv *env, jobject obj, jstring chaine1, jstring chaine2){
							char resultat[256];
							const char *str1 = (*env)-&gt;GetStringUTFChars(env, chaine1, 0);
							const char *str2 = (*env)-&gt;GetStringUTFChars(env, chaine2, 0);
							sprintf(resultat,"%s%s", str1, str2);
							(*env)-&gt;ReleaseStringUTFChars(env, chaine1, str1);
							(*env)-&gt;ReleaseStringUTFChars(env, chaine2, str2);
							return (*env)-&gt;NewStringUTF(env, resultat);
							}</stdio.h>
                    </jni.h>
                </textarea>
                <p>
                    <font color="red">
                        <b>Attention</b>
                    </font>
                    : ce code est très simpliste car il ne vérifie pas un éventuel débordement du tableau résultat.
					Après la compilation des différents éléments, l'exécution affiche le résultat escompté.
                </p>
            </div>
        </div>
        <div class="chapter">
            <h2>La gestion des erreurs</h2>
            <p>
                Les méthodes natives constituent un risque significatif au niveau de la sécurité des programmes en langage Java.
				Le système d'exécution C n'a aucune protection contre les erreurs de limites de tableau, l'indirection via des pointeurs erronés,etc.
				Il est particulièrement important que les programmeurs de méthodes natives gèrent toutes les conditions d'erreur afin de préserver l'intégrité de la
				plate-forme Java.
				En particulier, lorsque votre méthode native diagnostique un problème qu'elle ne peut gérer, elle doit le signaler à une machine virtuelle de Java.
				Dans un tel cas, vous lanceriez alors naturellement une exception. Cependant, il n'y a pas d'exceptions en C.
				A la place, vous devez appeler la fonction
                <i>Throw</i>
                ou
                <i>ThrowNew</i>
                pour créer un nouvel objet exception.
				Lors de la sortie de la méthode native, la machine virtuelle de Java va lancer cette exception.
            </p>
            <p>
                Pour utiliser la fonction
                <i>Throw</i>
                , appelez
                <i>NewObject</i>
                pour créer un objet d'un sous-type de
                <i>Throwable</i>
                .
				Par exemple, ici, nous allouons un objet
                <i>EOFException</i>
                et nous le lançons.
            </p>
            <textarea name="code" class="c">
                jclass class EOFException=(*env)-&gt;FindClass(env,"java/io/EOFException");
				jmethodID id_EOFException=(*env)-&gt;GetMethodID(env,class_EOFException,"
                <init>","()V); /* ID du constructeur par défaut */
					jthrowable obj_exc=(*env)-&gt;NewObject(env,classEOFException,id_EOFException);
					(*env)-&gt;Throw(env,obj_exc); (*env)-&gt;NewStringUTF(env, resultat);
					}</init>
            </textarea>
            <p>
                Il est généralement plus pratique d'appeler
                <i>ThrowNew</i>
                , qui construit un objet exception, à partir d'une classe et d'une chaîne UTF.
				Ces deux méthodes n'interrompent pas le flux de la méthode native. Ce n'est que lors du retour de la méthode que la machine virtuelle lance réellement
				l'exception.
				Par conséquent, chaque appel à
                <i>Throw</i>
                et
                <i>ThrowNew</i>
                doit toujours être suivi immédiatement d'une instruction
                <i>return</i>
                .
            </p>
        </div>
        <div class="chapter">
            <h2>Conclusion</h2>
            <p>Bien que nous ayons vu l'impossibilité des méthodes natives à être portables, nous avons par contre pu constater le caractère interopérable du langage Java
				avec d'autres langages exécutables.
				Ainsi, le programmeur dispose d'une certaine flexibilité quant à l'utilisation de Java avec un langage exécutable même si en termes de syntaxe cela s'avère
				plus difficile à réaliser</p>
        </div>
    </body>
</html>

